home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / info-service / www / src / WWW / Daemon / Implementation / TechInfoGate.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-12  |  22.3 KB  |  971 lines

  1. /*         WWW  --> TechInfo Gateway
  2.   
  3.   
  4.   Copyright November 1992 by the University of Pennsylvania.
  5.   Copyright 1992, 1993 CERN
  6.  
  7. Authors:
  8.     LAM    Linda Murphy, University of Pennsylvania
  9.     TBL    Tim Berners-Lee, CERN
  10.  
  11. History:
  12.        Dec 92    WWW gateway made (TBL) 
  13.        Nov 92    Written as Gopher Gateway (LAM)
  14. */
  15. /* 
  16.      Gateway from World-Wide Web  to TechInfo Services
  17.  
  18.    This is a one way gateway from WWW protocol to TI protocol:
  19.    WWW client connects to this server and makes a request, this
  20.    program connects to a TechInfo server and gets the appropriate
  21.    information, sends the response to the client, and then closes 
  22.    the connection.
  23.  
  24.    Request from WWW client              This program's response
  25.    -----------------------              -----------------------
  26.  
  27. GET /                                   Local TechInfo Server's main menu, 
  28.                                         WorldWide TechInfo, and,
  29.  
  30. GET /tihost:tiport/type/tinodeid         Results of Techinfo transaction
  31.  
  32. GET /tihost:tiport/type/tinodeid?word+word
  33.                                          Results of Techinfo search
  34.  
  35. GET /doc/worldwide-techinfo              List of TechInfo servers
  36.  
  37. GET /doc/WWW-TI-Gateway                  Document -- about this server
  38.  
  39.     Note that the gateway will also accept a
  40.    Examples:
  41.    
  42.   
  43.  
  44.        Input:
  45. GET /
  46.  
  47.        Output:
  48.  
  49. <title>WorldWideWebTechInfo gateway</title>
  50. <h1>Techinfo Gateway</h1>
  51. Welcome to the WWW techinfo gateway. Information available
  52. through this service is that served by servers running the
  53. TechInfo protocol from MIT.
  54. See also:
  55. <MENU>
  56. <LI><A HREF="/doc/WWW-TI-Gateway">About this gateway</A>
  57. <LI><A HREF="/ti-srv.upenn.edu:9000/M/836">About PennInfo</A>
  58. <LI><A HREF="/ti-srv.upenn.edu:9000/D/7182">Today's Pennsylvania weather</a>
  59. <LI><A HREF="/doc/worldwide-techinfo">World Wide TechInfo</a>
  60. </MENU>
  61.  
  62.     Input:
  63.  
  64. GET /doc/worldwide-techinfo
  65.  
  66.     Output:
  67. <TITLE>World Wide TechInfo</TITLE>
  68. <MENU>
  69. <LI><A href="/tiserve.mit.edu:9000/M/0">MIT</A>
  70. <LI><A href="/xantos.uio.no:9000/M/0">University of Oslo</A>
  71. </MENU>
  72.  
  73.     Input:
  74. GET /ti-srv.upenn.edu:9000/M/0
  75.  
  76.        Output:
  77.  
  78. <TITLE>whatever the title of node 0 is
  79. <ISINDEX>
  80. <MENU>
  81. <LI><A HREF="//ti-srv.upenn.edu:9000/M/836>About PennInfo</A>
  82. <LI><A HREF="/ti-srv.upenn.edu:9000/D/7182">Today's Pennsylvania weather</a>
  83. </MENU>
  84.  
  85.     Note:  The <ISINDEX> means that a search can be made by the user
  86.     who is at this node.
  87.     
  88.  
  89.    Input:
  90.  
  91. GET /ti-srv.upenn.edu:9000/D/7182
  92.  
  93.    Output:
  94. <PLAINTEXT>
  95. {ascii text document}
  96.  
  97.  
  98.    Input:    (Is this a TI search? - Tim)
  99.    
  100. GET /ti-srv.upenn.edu:9000/M/120?Internet+Gopher
  101.  
  102.    Output:
  103.  
  104. <TITLE>"Internet Gopher" under (title of node 120)</TITLE>
  105. <ISINDEX>
  106. <MENU>
  107. <LI><A HREF="/ti-srv.upenn.edu:9000/D/3568">
  108. The Internet Gopher - What is it?
  109. </A>
  110. </MENU>
  111.  
  112.  
  113.    This program is not standalone -- it's meant to be called from
  114.    inetd.
  115.  
  116. */
  117.  
  118.  
  119. /* CODE STARTS HERE ---   */
  120.  
  121. #ifdef OLD_INCLUDES
  122. #include <stdio.h>
  123. #include <strings.h>
  124. #include <errno.h>
  125. #include <sys/types.h>
  126. #include <sys/socket.h>
  127. #include <netinet/in.h>
  128. #include <netdb.h>
  129. #else
  130. #include "HTUtils.h"
  131. #include "tcp.h"        /* All system specific file, net etc */
  132. #endif
  133.  
  134. #include <ctype.h>
  135.  
  136. #define LOCALTI_SERVER      "penninfo-srv.upenn.edu"
  137. #define LOCALTI_PORT          "9000"
  138. #define LOCALTI_MAINMENU   "0"
  139. #define DEBUGLOG "/usr/users/murphy/www_ti_gw.log" 
  140. #define MSGFILE  "/usr/users/murphy/www_ti_gw.msg"
  141. #define GWVERSION "WWW-TI_GATE:1.1"
  142.  
  143. /* Which host is the keeper of the TechInfo servers worldwide? */
  144.  
  145. #ifndef TISERVERS_HOST
  146. #define TISERVERS_HOST  "tiserve.mit.edu"
  147. #endif
  148.  
  149. #ifndef TISERVERS_PORT
  150. #define TISERVERS_PORT  "9000"
  151. #endif
  152.  
  153. #define FLUSHLINES 10        /* Flush menus every 10 lines */
  154.  
  155. PUBLIC FILE * logfile = 0;    /* Log file if any */
  156. PUBLIC char *HTClientHost;    /* Peer internet address */
  157.  
  158.  
  159. /*    Special document IDs
  160. */
  161.  
  162. #define ABOUT_GW      "/WWW-TI-Gateway"      /* About this gopher */
  163. #define TERMBASEDTI   "/telnet-techinfo"     /* Telnet session to techinfo */
  164. #define WORLDWIDETI   "/worldwide-techinfo"  /* List of all TechInfo servers */
  165.  
  166. /*    Forward declarations
  167. */
  168. PRIVATE int send_msg();
  169. PRIVATE void logdebug();
  170. PRIVATE void send_version();
  171. PRIVATE int get_msg();
  172.  
  173.  
  174. /*        Module-wide Variables
  175. */
  176. PRIVATE FILE * client;
  177.  
  178. int gophinsock = -1;
  179.  
  180. char tibuf[200000];
  181. char clientbuf[10000];
  182.  
  183.  
  184. #define CR '\r'
  185. #define LF '\n'
  186. #define EOM             ".\r\n"
  187. #define EOM_LEN         (sizeof(EOM) -1)
  188. #define    terminator(str)    (!strncmp(str, EOM, EOM_LEN))
  189.  
  190. #ifndef RUNTIME_UID
  191. #define RUNTIME_UID 1   /* 1 is usually the DAEMON */
  192. #endif
  193.  
  194. /* TechInfo Protocol */
  195. #define TICMD_GETSERVERS        "m"
  196. #define TICMD_GETMENU           "w:2:%s:1"
  197. #define TICMD_GETDOC            "t:%s:%d:%d"
  198. #define TICMD_KEYSRCH           "b:%s"
  199. #define TICMD_KEYSRCH_NODEID    "b:%s:%s"
  200. #define TICMD_VERSION           "v:%s"
  201. #define TIOKAY                  "0:"
  202. #define TIMAXFIELDS             40
  203. #define TIDELIM                 ':'
  204. #define TOTCHARSTR              "Total Characters :"
  205.  
  206. /* Intermediary protocol -- definitions of urls for this gateway  */
  207. #define I_DELIM         '/'
  208. #define I_DOC           'D'
  209. #define I_MENU          'M'
  210.  
  211.  
  212. /*    Read a line from given socket
  213. **
  214. **  On entry,
  215. **    line    contains enough space
  216. **    sz    size of the line array
  217. **    *idx    points to start of space
  218. **    sock    is already connected and open for read
  219. **
  220. ** On exit,
  221. **    return -40    error
  222. **        0    OK
  223. **    *idx        points to terminating zero in line
  224. **    line        conatins new data
  225. */
  226. PRIVATE int readline (char *line, int sz, int sock, int *idx)
  227. {
  228.   int rc;
  229.  
  230.   *idx = 0;
  231.   line[*idx] = 0;
  232.   do {
  233.     rc = read(sock, line + *idx, 1);
  234.     if (rc < 0) {
  235.       perror ("reading line");
  236.       return (-40);
  237.     }
  238.     (*idx)++;
  239.   } while (*idx < sz-1 && line[*idx-1] != LF);
  240.   line[*idx] = 0;
  241.   return 0;
  242. }
  243.  
  244.  
  245.  
  246.  
  247. /*        Send TI document back to client        send_ti_doc()
  248. **
  249. **
  250. ** On entry,
  251. **    tisock        connected to server
  252. **    nodeid        points to node valid id string
  253. **
  254. ** On exit,
  255. **    returns        number of bytes sent excluding header
  256. **    document retrieved and relayed.
  257. **
  258. ** The buffers are flushed often at first and then with decreasing frequency
  259. ** to give good pipelining performance.
  260. */
  261.  
  262. PRIVATE int send_ti_doc (int tisock, char *nodeid)
  263. {
  264.   char docbuf[20200];
  265. #define DOC_BLKSZ (sizeof(docbuf)-200)
  266. /* 200 extra chars for first line from TI server, contains lastmodified, etc */
  267.   long startat;
  268.   long totalchars;
  269.   char *begintext, *endline;
  270.   long totalsent = 0;
  271.   int wrote;
  272.   int flushlevel = 1000;    /* Flush after this times 2**k */
  273.  
  274.   startat = 0;
  275.   do {
  276.     sprintf (tibuf, TICMD_GETDOC, nodeid, (int)startat, (int)DOC_BLKSZ);
  277.     sprintf (docbuf, "ToTechinfo:%s", tibuf);
  278.     logdebug (docbuf);
  279.     send_msg (tisock, tibuf, strlen(tibuf));
  280.     
  281.     /* get response to the t: command */
  282.     get_msg (tisock, docbuf, sizeof(docbuf));
  283.  
  284.     if (startat == 0) {
  285.       totalchars = atoi (docbuf);
  286.     }
  287.  
  288.     /* Don't know why, but in response to t: transaction,
  289.        the TI server sends a status line ending in LF, 
  290.        and then sends another LF -LAM */
  291.     for (begintext = docbuf; *begintext != LF && *begintext;
  292.      begintext++);
  293.     if (*begintext == LF) begintext++;
  294.     if (*begintext == LF) begintext++;
  295.  
  296.     *(begintext + DOC_BLKSZ) = '\0'; /* eliminating possible LF at end */
  297.     
  298.     fprintf(client, "<TITLE>(TechInfo document %s)</TITLE>\r\n", nodeid);
  299.     /* @@ Not very informative! -- can get real title? */
  300.     fprintf(client, "<PLAINTEXT>\r\n");
  301.     
  302.     for (; *begintext != '\0'; ) {
  303.       for (endline=begintext; *endline && *endline != LF; endline++);
  304.       wrote = fwrite(begintext, 1, endline-begintext, client);
  305.       if (wrote > 0) totalsent += wrote;
  306.  
  307.       if (*endline == LF) {
  308.     wrote = fwrite("\r\n", 1, 2, client);
  309.     if (wrote > 0) totalsent += wrote;
  310.     endline++;
  311.     if (totalsent > flushlevel) {
  312.         fflush(client);
  313.         flushlevel = flushlevel + flushlevel;  /* Grow exponentially */
  314.     }
  315.       }
  316.       begintext = endline; 
  317.     }
  318.  
  319.     startat = startat + DOC_BLKSZ;
  320.   } while (totalchars - startat > 0);
  321.  
  322.   sprintf (docbuf, "Sent %d bytes", (int)totalsent);
  323.   logdebug (docbuf);
  324.  
  325.   if (totalsent < 1) {
  326.     fprintf (client,
  327.     "TechInfo gateway warning: Document seems to be nonexistent or empty.\r\n");
  328.     fflush(client);
  329.   }
  330.   return totalsent;
  331. }
  332.  
  333.  
  334. PRIVATE int parse_fields (char delim, char *line, char *fields[], int maxfields)
  335. {
  336.   char *cp = line;
  337.   int argcnt = 0;
  338.  
  339.   while (argcnt < maxfields) {
  340.     fields[argcnt] = cp;
  341.     argcnt++;
  342.     while (*cp && *cp != delim)
  343.       cp++;
  344.     if (!*cp)
  345.       break;
  346.     *cp = 0;        /* Break fields apart */
  347.     cp++;
  348.   }
  349.   fields[argcnt] = (char *) 0;
  350.   return argcnt;
  351. }
  352.  
  353.  
  354. PRIVATE struct hostent *
  355. gethostbyn_or_ad (host)
  356.      char *host;
  357. {
  358.   u_long ip_addrl;
  359.   struct hostent *h;
  360.  
  361.   if (isdigit (host[0])) {     /* if name begins with a digit, assume IP adr */
  362.     ip_addrl = inet_addr (host);
  363.     h = gethostbyaddr ((char *)&ip_addrl, (int)sizeof(u_long), AF_INET);
  364.     return (h);
  365.   }
  366.   else {
  367.     h = gethostbyname (host); 
  368.     return (h);
  369.   }
  370. }
  371.  
  372.  
  373. PRIVATE int getportbyn_or_num (str, port)
  374.      int *port;
  375.      char *str;
  376. {
  377.   struct servent *servent;
  378.  
  379.   if ( isdigit(str[0]) ) {   /* if its a number, convert ascii to int */
  380.     *port = atoi(str);
  381.     *port = htons( (u_short) *port);
  382.   }
  383.   else {        /* if not a number, look up name in Ultrix */
  384.     servent = getservbyname (str, "tcp");
  385.     if (servent == 0) {
  386.       return (-1);
  387.     }
  388.     *port = servent->s_port;
  389.   }
  390.   return (1);
  391. }
  392.  
  393.  
  394.  
  395.  
  396.  
  397. PRIVATE int
  398. inet_connect(char *hostname, char *service, char **errstr)
  399. {
  400. #define ERRORMSG_SIZE 200
  401.   struct hostent *host;
  402.   int toport;
  403.   struct sockaddr_in sin;
  404.   int sock;
  405.   extern char *sys_errlist[];
  406.   extern int sys_nerr;
  407.  
  408.   *errstr = (char *) malloc (ERRORMSG_SIZE);
  409.   (*errstr)[0] = 0;
  410.  
  411.   host = gethostbyn_or_ad (hostname);
  412.  
  413.   if (host == NULL) {
  414.     sprintf (*errstr, "%s: Unknown host", hostname);
  415.     return -1;
  416.   }
  417.  
  418.   else  if (getportbyn_or_num (service, &toport) < 0)  {
  419.     sprintf (*errstr, "%s: Unknown TCP service", service);
  420.     return -1;
  421.   }
  422.  
  423.   else {
  424.     sin.sin_family = host->h_addrtype;
  425.     bcopy(host->h_addr,  &(sin.sin_addr), host->h_length);
  426.     sin.sin_port = toport;
  427.  
  428.     sock = socket (AF_INET, SOCK_STREAM, 0); /* Had extra 0 parameter - TBL */
  429.     if (sock < 0) {
  430.       strcat (*errstr, "socket: ");
  431.      }
  432.  
  433.     else {
  434.       if (connect (sock, (struct sockaddr*)&sin, (int)sizeof (sin)) < 0) {
  435.     strcat (*errstr, hostname);
  436.     strcat (*errstr, ":");
  437.     strcat (*errstr, service);
  438.     strcat (*errstr, ":");
  439.     close (sock);
  440.     sock = -1;
  441.       }
  442.     }
  443.   } 
  444.  
  445.   if (errno >= 0 && errno < sys_nerr)  {
  446.     strcat (*errstr, sys_errlist[errno]);
  447.   }
  448.  
  449.   if (sock >= 0)
  450.     free(*errstr);
  451.   return (sock);
  452. }
  453.  
  454. PRIVATE int 
  455. send_msg(sock,str,len) /* BEWARE! this routine writes into str beyond len! */
  456.      int sock;
  457.      char *str;
  458.      int len;
  459. {
  460.   int wrote;
  461.  
  462.   if (sock < 0) return 0;
  463.   if (len < 1) return 0;
  464.  
  465.   str[len] = CR;
  466.   str[len+1] = LF;
  467.   if (TRACE) fprintf(stderr, "TIGATE: sending to socket %d: %s", sock, str);
  468.   wrote = write(sock, str, len+2);
  469.   if (wrote != len+2) {
  470.     fprintf (stderr, "Wrote %d instead of %d, errno %d", wrote, len+2, errno);
  471.     perror ("writing message");
  472.     return (-50);
  473.   }
  474.   else
  475.     return 1;
  476. }
  477.  
  478.  
  479.  
  480. PRIVATE int get_msg(sock, buff, buffsize)   /* read message off socket */
  481.      int             sock;
  482.      char           *buff;
  483.      int             buffsize;
  484. {
  485.   int rc, len;
  486.  
  487.   len = 0;
  488.   bzero (buff, buffsize);
  489.  
  490.   do {
  491.     rc = read(sock, &buff[len], buffsize - len);
  492.     if (rc < 0 ) {
  493.       perror("in read");
  494.       break;
  495.     }
  496.     len = rc + len;
  497.   } while (rc >= 0 && len <= buffsize && !terminator(&buff[len - EOM_LEN]));
  498.  
  499.   if (terminator (&buff[len - EOM_LEN]))
  500.     len -= EOM_LEN;
  501.  
  502.   buff[len] = '\0';
  503.   return (rc);
  504. }
  505.  
  506.  
  507.  
  508.  
  509. PRIVATE void logdebug (char *string)
  510. {
  511. #ifdef DEBUG
  512.   FILE *debugfp;
  513.   long now;
  514.   struct sockaddr_in sname;
  515.   struct hostent *host;
  516.   char peername[100];
  517.   int namelen;
  518.  
  519.   namelen = sizeof(sname);
  520.   
  521.   if (getpeername (gophinsock, (struct sockaddr*)&sname, &namelen)) 
  522.     sprintf (peername, "getpeername-errno-%d", errno);
  523.   else {
  524.     host = gethostbyaddr((char*)&sname.sin_addr, sizeof(sname.sin_addr), AF_INET);
  525.     if (!host)
  526.       sprintf (peername, "gethostbyaddr errno %d", errno);
  527.     else
  528.       sprintf (peername, "%s", host->h_name);
  529.   }
  530.  
  531.   debugfp = fopen (DEBUGLOG, "a");
  532.   if (debugfp != NULL) {
  533.     now = time(0);
  534.     fprintf (debugfp, "%d:%s:%s:%s", getpid(), string, peername, ctime (&now));
  535.     fclose(debugfp);
  536.   }
  537. #endif
  538. }
  539.  
  540.  
  541. /*    Make MENU item for W3 menu document        make_menu_item()
  542. **    -----------------------------------
  543. */
  544. PRIVATE void send_menu_line (char *title, char ityp,
  545.         char *tiserver, char *tiport, char *nodeid)
  546. {
  547.     fprintf (client, "<LI><A HREF=\"/%s:%s/%c/%s\">%s</A>\r\n",
  548.       tiserver, tiport, ityp, nodeid, title);
  549. }
  550.  
  551.  
  552. /*    Relay one item from a list            send-server_line()
  553. **    --------------------------
  554. */
  555. PRIVATE  void send_server_line(char *line)
  556. {
  557.   char *fields[TIMAXFIELDS];
  558.   char *nodeid, *server, *port, *title;
  559.   char *Title;
  560.   
  561.   parse_fields (TIDELIM, line, fields, TIMAXFIELDS);
  562.   nodeid = fields[0];
  563.   port = fields[1];
  564.   title = fields[5];
  565.   server = fields[6];
  566.  
  567.   Title = (char *) malloc (strlen(title) +  strlen (" TechInfo") + 1);
  568.   strcpy (Title, title);
  569.   strcat (Title, " TechInfo");
  570.  
  571.   send_menu_line (Title, I_MENU, server, port, nodeid);
  572.  
  573. }
  574.  
  575.  
  576. /*    Relay the list of all TI servers        send_ti_server_list()
  577. **
  578. **    The master server address is built into the gateway.
  579. */
  580.  
  581. PRIVATE void send_ti_server_list()
  582. {
  583.   char *errstr;
  584.   int length;
  585.   int toread, numlines;
  586.   int tisock;
  587.  
  588.  
  589.   logdebug ("Send server list");
  590.  
  591.   tisock = inet_connect (TISERVERS_HOST, TISERVERS_PORT, &errstr);
  592.   if (tisock < 0) {
  593.     fprintf(client,
  594.     "Gateway Error: Couldn't get list of TechInfo servers. %s (port %s)\r\n",
  595.     errstr, TISERVERS_PORT);
  596.   } else {
  597.     get_msg (tisock, tibuf, sizeof(tibuf));
  598.     send_version(tisock);    /* waht does this do? - Tim */
  599.  
  600.     sprintf (tibuf, "%s", TICMD_GETSERVERS);
  601.     logdebug (tibuf);
  602.  
  603.     send_msg (tisock, tibuf, strlen(tibuf));
  604.     readline (tibuf, sizeof(tibuf), tisock, &length);
  605.  
  606.     toread = atoi (tibuf);
  607.     numlines = 0;
  608.     fprintf(client, "<MENU>\r\n");
  609.     while (numlines < toread) {
  610.       readline (tibuf, sizeof(tibuf), tisock, &length);
  611.       numlines++;
  612.       send_server_line(tibuf);
  613.     }
  614.     fprintf(client, "</MENU>\r\n");
  615.   }
  616. }
  617.  
  618.  
  619. /*    Send one menu item                send_menu_item()
  620. **
  621. ** On entry,
  622. **    line            contains the TI menu line
  623. **    tiserver        contains the server fqdn or number
  624. **    tiport            contains the port number
  625. **    minlevel        ??
  626. **
  627. **
  628. ** On return,
  629. **    returns 0        level too low,
  630. **        1        line sent to client,
  631. */
  632.  
  633. PRIVATE int send_menu_item (char *line, char *tiserver, char *tiport, int minlevel)
  634. {
  635.   char *fields[TIMAXFIELDS];
  636.   char *title, *filename, *nodeid, *level;
  637.  
  638.   parse_fields (TIDELIM, line, fields, TIMAXFIELDS);
  639.   level = fields[0];
  640.   nodeid = fields[1];
  641.   title = fields[5];
  642.   filename = fields[8];
  643.   
  644.   if (atoi(level) < minlevel)
  645.     return 0;
  646.   
  647.   send_menu_line (title, strlen(filename) > 0 ? I_DOC : I_MENU,
  648.           tiserver, tiport, nodeid);
  649.   return 1;
  650. }
  651.  
  652.  
  653.  
  654. /*    Send the search information and/or link to server top
  655. **
  656. **   This tells the user what a search will do. For a non-top node,
  657. **   it gives a link to the top.
  658. */
  659. PRIVATE void send_keywordsearch_item
  660.  (char *tiserver, char *tiport, char *nodeid, int numitems)
  661. {
  662.   if ((strlen(nodeid) > 0) && strcmp(nodeid, "0")!=0) {
  663.     fprintf(client,
  664.         "%s\r\nSee also the <A HREF=\"/%s:%s/M/0\">whole server</A>.\r\n",
  665.         "Supply keywords to search the information under this menu.",
  666.     tiserver, tiport);
  667.   } else {
  668.     fprintf (client, 
  669.       "%s\r\n(This server is at %s on port %s.)\r\n",
  670.       "Supply keywords to search all information at this TechInfo server",
  671.          tiserver, tiport);
  672.   }
  673.  
  674.  
  675. }
  676.  
  677.  
  678.  
  679.  
  680. PRIVATE void send_ti_nodelist (int tisock, char *tiserver, char *tiport, int *sent, int minlevel)
  681. {
  682.   int toread, numlines;
  683.   int length;
  684.  
  685.   readline (tibuf, sizeof(tibuf), tisock, &length);
  686.   toread = atoi (tibuf);
  687.   numlines = 0;
  688.   *sent = 0;
  689.   while (numlines < toread) {
  690.     readline (tibuf, sizeof(tibuf), tisock, &length);
  691.     /*    logdebug (tibuf); */
  692.     numlines++;
  693.     *sent += send_menu_item (tibuf, tiserver, tiport, minlevel);
  694.     if ((numlines % FLUSHLINES) == 0) fflush(client);
  695.   }
  696.   sprintf (tibuf, "Sent %d nodes", *sent);
  697.   logdebug (tibuf);
  698.   readline (tibuf, sizeof(tibuf), tisock, &length);
  699. }
  700.  
  701. /*                Special Cases
  702. */
  703.  
  704. /*        Welcome Page
  705. **
  706. */
  707. PRIVATE int welcome()
  708. {
  709.     fprintf(client, "<TITLE>Welcome to the TechInfo Gateway</TITLE>\r\n");
  710.     fprintf(client, "<H1>TechInfo Gateway</H1>\r\n");
  711.     fprintf(client,
  712.     "Welcome to the WorldWideWeb-TechInfo gateway.<p>\r\n");
  713.     fprintf(client,
  714.     "Information available through this service is that\r\n");
  715.     fprintf(client,
  716.     "served by servers running the TechInfo protocol from MIT.\r\n");
  717.     fprintf(client,
  718.     "<P>Servers running this protocol currently include the following:\r\n");
  719.     fprintf(client, "\r\n");
  720.     fflush(client);
  721.  
  722.     send_ti_server_list();
  723.  
  724.     fflush(client);
  725.     return 1;
  726. }
  727.  
  728.  
  729. /* The page below may be best just written as a hypertext file - Tim
  730. */
  731.  
  732. PRIVATE int terminalbased_ti()
  733. {
  734.   char * fmt = "<LI><A HREF=\"telnet://%s/\">%s</A>\r\n";
  735.   fprintf(client,
  736.   "<title>Terminal-based Techinfo Servers</title>\r\n");
  737.   fprintf(client,
  738.   "<H1>Terminal-based Techinfo Servers</H1>\r\n");
  739.   fprintf(client, "<MENU>\r\n");
  740.   fprintf(client, fmt,
  741.         "techinfo.mit.edu", "Massachusetts Institute of Technology");
  742.   fprintf(client, fmt,
  743.       "msuinfo.msstate.edu", "Mississippi State");
  744.   fprintf(client, fmt,
  745.       "penninfo.upenn.edu", "University of Pennsylvania");
  746.   fprintf(client, fmt,
  747.       "penninfo.upenn.edu:9005", "University of Pennsylvania TEST");
  748.   fprintf(client, "</MENU>\r\n");
  749.   fflush(client);
  750.   return 1;
  751. }
  752.  
  753. PRIVATE int do_about_gw()
  754. {
  755.   FILE *fp;
  756.   char line[100], *cp;
  757.  
  758.   fp = fopen (MSGFILE, "r");
  759.   if (fp == NULL) {
  760.     fprintf (client, "Unable to read msg file %s\r\n", MSGFILE);
  761.     fflush(client);
  762.     exit (-2);
  763.   }
  764.   do {
  765.     if (fgets (line,sizeof(line)-1,fp) == NULL)
  766.       break;
  767.     else {
  768.       cp = index (line, '\n');
  769.       if (cp) {
  770.     fwrite (line, 1, cp-line, client);
  771.     fprintf(client, "\r\n");
  772.       }
  773.       else
  774.     fprintf(client, line);
  775.     }
  776.   } while (1);
  777.   return 1;
  778. }
  779.  
  780.  
  781. /*        Retrieve a document
  782. **
  783. **  This routine is called either by the test program at the bottom of
  784. **  this file or by the HTDaemon code.
  785. **
  786. ** On entry,
  787. **    soc        A file descriptor for input and output.
  788. ** On exit,
  789. **    returns        >0    Channel is still open.
  790. **            0    End of file was found, please close file
  791. **            <0    Error found, please close file.
  792. */
  793.  
  794. PUBLIC int HTRetrieve(char *request, char *keywords, int soc)
  795. {
  796.     char *fields[5];        /* allow one extra for zero term! TBL*/
  797.     int tisock;
  798.     char *tiserver, *tiport, *typ, *nodeid;
  799.     char *errstr;
  800.     int numitems;
  801.     
  802.     client = fdopen(soc, "w");        /* open normal C file pointer */
  803.     if (!client) return -1;
  804.  
  805.     if (request[1] == 0)                 /* is it a single / ? */
  806.     return welcome();
  807.     
  808.     if (!strcmp(request, ABOUT_GW))
  809.         return do_about_gw();
  810.  
  811.     if (!strcmp(request, TERMBASEDTI))
  812.         return terminalbased_ti();
  813.  
  814.              /* should be a techinfo menu request */
  815.  
  816.     if (parse_fields ('/', request, fields, 4) < 3) {
  817.     fprintf(client,
  818.     "Gateway error: Bad document ID\r\n");
  819.         fflush(client);
  820.     return 1;
  821.     }
  822.     
  823.     tiserver = fields[1];
  824.     typ = fields[2];
  825.     nodeid = fields[3];
  826.     if (!nodeid) nodeid="0";
  827.     
  828.     parse_fields(':', tiserver, fields, 2);
  829.     tiport = fields[1];
  830.     if (!tiport) tiport = "9000";    /* Default port? @@ */
  831.  
  832.  
  833. /* okay, we've parsed the Intermed. token... */
  834.  
  835.     tisock = inet_connect (tiserver, tiport, &errstr);
  836.     if (tisock < 0) {
  837.     fprintf(client,
  838.     "Gateway error: Couldn't connect to Techinfo:%s (port %s)\r\n",
  839.         errstr, tiport);
  840.         fflush(client);
  841.     return 1;
  842.     }
  843.     
  844.     get_msg (tisock, tibuf, sizeof(tibuf));
  845.     send_version(tisock);
  846.  
  847.     if (keywords) {                /* Search */
  848.     
  849.         fprintf(client, "Information with keywords `%s' in this index\r\n",
  850.         keywords);
  851.     fflush(client);    /* Raise expectations by printing something */
  852.     
  853.     if ((strlen(nodeid) > 0) && (strcmp(nodeid, "0")!=0))
  854.           sprintf (tibuf, TICMD_KEYSRCH_NODEID, keywords, nodeid);
  855.     else
  856.           sprintf (tibuf, TICMD_KEYSRCH, keywords);
  857.  
  858.     send_msg (tisock, tibuf, strlen(tibuf));
  859.  
  860.     if (numitems == 0) {
  861.         fprintf(client,
  862.             "Sorry, no information with those keywords.<p>\r\n");
  863.     }
  864.     
  865.     fprintf(client, "<ISINDEX>\r\n<MENU>\r\n");
  866.     send_ti_nodelist(tisock, tiserver, tiport, &numitems, 0);
  867.     fprintf(client, "</MENU>\r\n");
  868.  
  869.     } else if (*typ == I_DOC) {            /* Relay document */
  870.     send_ti_doc (tisock, nodeid);
  871.     
  872.     } else {                     /* Relay menu */
  873.  
  874.     sprintf (tibuf, TICMD_GETMENU, nodeid);
  875.     send_msg (tisock, tibuf, strlen(tibuf));
  876.  
  877.     fprintf(client, "<ISINDEX>\r\n<MENU>\r\n");
  878.     send_ti_nodelist (tisock, tiserver, tiport, &numitems, 1);
  879.     fprintf(client, "</MENU>\r\n");
  880.     send_keywordsearch_item (tiserver, tiport, nodeid, numitems);
  881.     }
  882.  
  883.     fflush(client);
  884.     return 1;
  885. }
  886.  
  887. PRIVATE void send_version(int tisock)
  888. {
  889.   sprintf (tibuf, TICMD_VERSION, GWVERSION);
  890.   send_msg (tisock, tibuf, strlen(tibuf));
  891.   get_msg(tisock, clientbuf, sizeof(clientbuf));
  892. }
  893.  
  894. #ifdef TEST        /* Otherwise, use HTDaemon.c */
  895.  
  896. /*        Get request line                   gerreq()
  897. */
  898.  
  899. PRIVATE void getreq (char *request, int max, int insock)
  900. {
  901.   int idx;
  902.   char *cp;
  903.  
  904.   readline (request, max, insock, &idx);
  905.   cp = index (request, CR);
  906.   if (cp) *cp = 0;
  907.   cp = index (request, LF);
  908.   if (cp) *cp = 0;
  909. }
  910.  
  911.  
  912. /*        M A I N     P R O G R A M
  913. **        =======        =============
  914. **
  915. **   See also the HTDaemon.c file fro a more complete daemon shell.
  916. */
  917. void main (int argc, char *argv[])
  918. {
  919.     char request[1000];
  920.     char *ptr;
  921.     char * keywords;
  922.     
  923.     setreuid (RUNTIME_UID, RUNTIME_UID);
  924.     if (getuid() == 0 || geteuid() == 0) {
  925.         fprintf (stderr, 
  926.       "THIS SERVER NOT PERMITTED TO RUN AS PRIVILEGED UID\n");
  927.         fprintf(client, 
  928.       "THIS SERVER NOT PERMITTED TO RUN AS PRIVILEGED UID\r\n");
  929.         fflush(stderr);
  930.         fflush(client);
  931.     exit (-1);
  932.     }
  933.     
  934.     client = stdout;
  935.     gophinsock = fileno(stdin);
  936.     
  937.     
  938.     logdebug ("Start");
  939.  
  940. /*    (Note: needed to know own address as Gopher server, not needed
  941. **    for WWW server. - TBL )
  942. */
  943.  
  944.     getreq (request, sizeof(request), gophinsock);
  945.     
  946.     for (ptr = request; *ptr && isspace(*ptr) ; ptr++);
  947.     
  948.     if (strncmp(ptr, "GET ", 4)!=0) {
  949.     fprintf(client, "Unrecognised request format %s\r\n", ptr);
  950.     return;
  951.     }
  952.   
  953.     ptr = ptr +4;
  954.   
  955.     keywords = strchr(request, '?');
  956.     if (keywords) {
  957.     *keywords++ = 0;        /* Chop keywords off */
  958.     if (!*keywords) keywords = NULL;
  959.     else {
  960.         char *p = keywords;
  961.         while ((p = strchr(p, '+')) != 0) *p = ' '; /* Plusses to spaces */
  962.     }
  963.     }
  964.     
  965.     HTRetrieve(ptr, keywords, 1);
  966.     
  967.     logdebug ("End");
  968.     exit (0);
  969. }
  970. #endif /* TEST */
  971.